﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace Narwhunderful.Aphelia.Framework.IO.Serialization
{

    public abstract class Property
    {

        protected Property(string name, Type type)
        {
            Name = name;
            Type = type;
        }
        public string Name { get; set; }
        public Type Type { get; set; }
        public Property Parent { get; set; }

    }

    public sealed class NullProperty : Property
    {

        public NullProperty()
            : base(null, null)
        {
        }

        public NullProperty(string name)
            : base(name, null)
        {
        }

    }

    public sealed class PropertyCollection : Collection<Property>
    {

        public Property Parent { get; set; }

        protected override void ClearItems()
        {
            foreach (Property item in Items)
            {
                item.Parent = null;
            }
            base.ClearItems();
        }

        protected override void InsertItem(int index, Property item)
        {
            base.InsertItem(index, item);
            item.Parent = Parent;
        }

        protected override void RemoveItem(int index)
        {
            Items[index].Parent = null;
            base.RemoveItem(index);
        }

        protected override void SetItem(int index, Property item)
        {
            Items[index].Parent = null;
            base.SetItem(index, item);
            item.Parent = Parent;
        }
    }

    public sealed class SimpleProperty : Property
    {

        public SimpleProperty(string name, Type type)
            : base(name, type)
        {
        }

        public object Value { get; set; }

        public override string ToString()
        {
            string text = base.ToString();
            return Value != null
                       ? string.Format("{0}, ({1})", text, Value)
                       : string.Format("{0}, (null)", text);
        }
    }

    public class ComplexProperty : Property
    {
        private PropertyCollection _properties;

        public ComplexProperty(string name, Type type, object value)
            : base(name, type)
        {
            this.Value = value;
            this.ComplexReferenceId = 0; // assume it is not recursive (yet)
        }

        public ComplexProperty(string name, System.Type type)
            : this(name, type, null)
        {
        }

        public object Value { get; set; }
        public int ComplexReferenceId { get; set; }

        public bool IsReferencedMoreThanOnce
        {
            get
            {
                return ComplexReferenceId != 0;
            }
        }

        public PropertyCollection Properties
        {
            get
            {
                if (_properties == null) _properties = new PropertyCollection { Parent = this };
                return _properties;
            }
            set { _properties = value; }
        }
    }

    public class ComplexReferenceProperty : Property
    {
        public ComplexProperty ReferenceTarget { get; set; }

        public ComplexReferenceProperty(string name, ComplexProperty referenceTarget)
            : base(name, null)
        {
            this.ReferenceTarget = referenceTarget;
        }

        public ComplexReferenceProperty(string name)
            : this(name, null)
        {
        }
    }

    public sealed class CollectionProperty : ComplexProperty
    {
        private IList<Property> _items;

        public CollectionProperty(string name, Type type)
            : base(name, type)
        {
        }

        public IList<Property> Items
        {
            get
            {
                if (_items == null) _items = new List<Property>();
                return _items;
            }
            set { _items = value; }
        }

        public Type ElementType { get; set; }
    }

    public sealed class DictionaryProperty : ComplexProperty
    {
        private IList<KeyValueItem> _items;

        public DictionaryProperty(string name, Type type)
            : base(name, type)
        {
        }

        public IList<KeyValueItem> Items
        {
            get
            {
                if (_items == null) _items = new List<KeyValueItem>();
                return _items;
            }
            set { _items = value; }
        }

        public Type KeyType { get; set; }
        public Type ValueType { get; set; }

    }

    public sealed class SingleDimensionalArrayProperty : Property
    {
        private PropertyCollection _items;

        public SingleDimensionalArrayProperty(string name, Type type)
            : base(name, type)
        {
        }

        public PropertyCollection Items
        {
            get
            {
                if (_items == null) _items = new PropertyCollection { Parent = this };
                return _items;
            }
            set { _items = value; }
        }

        public int LowerBound { get; set; }
        public Type ElementType { get; set; }

    }

    public sealed class MultiDimensionalArrayProperty : Property
    {
        private IList<DimensionInfo> _dimensionInfos;
        private IList<MultiDimensionalArrayItem> _items;

        public MultiDimensionalArrayProperty(string name, Type type)
            : base(name, type)
        {
        }

        public IList<MultiDimensionalArrayItem> Items
        {
            get
            {
                if (_items == null) _items = new List<MultiDimensionalArrayItem>();
                return _items;
            }
            set { _items = value; }
        }

        public IList<DimensionInfo> DimensionInfos
        {
            get
            {
                if (_dimensionInfos == null) _dimensionInfos = new List<DimensionInfo>();
                return _dimensionInfos;
            }
            set { _dimensionInfos = value; }
        }

        public Type ElementType { get; set; }
    }

    public sealed class MultiDimensionalArrayItem
    {

        public MultiDimensionalArrayItem(int[] indexes, Property value)
        {
            Indexes = indexes;
            Value = value;
        }

        public int[] Indexes { get; set; }
        public Property Value { get; set; }

    }

    public sealed class DimensionInfo
    {

        public int LowerBound { get; set; }
        public int Length { get; set; }

    }

    public sealed class KeyValueItem
    {

        public KeyValueItem(Property key, Property value)
        {
            Key = key;
            Value = value;
        }

        public Property Key { get; set; }
        public Property Value { get; set; }

    }

}
